﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Xml;

namespace OPDSView
{
    public partial class MainForm : Form
    {
        public string CatalogHttpResult = null;
        public OPDSCatalog currentOPDSCatalog = null;
        public OPDSEntry SelectedEntry = null;
        public string currentOPDSCatalogHost = "";
        
        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            // populate Catalogs combobox and select current catalog
            var catalogList = new OPDSCatalogList();
            catalogList.PopulateCatalogsComboBox(CatalogsComboBox);
            
            // load new Catalog from combobox selected item, done in PopulateCatalogsCombobox 
            // by setting selectedIndex (PopulateCatalogTree() is called from SelectedIndexChanged event automatically)
         }

        private void PopulateCatalogTree()
        {
            if (!httpWorker.IsBusy)
            {
                currentOPDSCatalog = (OPDSCatalog) CatalogsComboBox.SelectedItem;

                httpWorker.DoWork += httpWorker_GetCatalogRoot;
                httpWorker.RunWorkerCompleted += httpWorker_GetCatalogRootCompleted;
                httpWorker.RunWorkerAsync();
            }
        }

        private void httpWorker_GetCatalogRootCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            httpWorker.DoWork -= httpWorker_GetCatalogRoot;
            httpWorker.RunWorkerCompleted -= httpWorker_GetCatalogRootCompleted;

            if (e.Error != null)
            {
                showErrorMessage(e.Error.Message);
                return;
            }
            

            if (CatalogHttpResult != null)
            {
                try
                {
                    XmlDocument xml = new XmlDocument();
                    string filter = @"xmlns(:\w+)?=""([^""]+)""|xsi(:\w+)?=""([^""]+)""";   // replacing damn namespaces
                    CatalogHttpResult = Regex.Replace(CatalogHttpResult, filter, "");


                    // saving root xml to file
#if DEBUG
                    System.IO.File.WriteAllText(@"root.xml", CatalogHttpResult);
#endif

                    xml.LoadXml(CatalogHttpResult);
                    currentOPDSCatalog.Title = XmlHelper.GetInnerText(xml.DocumentElement.SelectSingleNode("/feed/title"), "");
                    currentOPDSCatalog.Id = XmlHelper.GetInnerText(xml.DocumentElement.SelectSingleNode("/feed/id"), "");
                    currentOPDSCatalog.Updated = XmlHelper.GetInnerText(xml.DocumentElement.SelectSingleNode("/feed/updated"), "");
                    currentOPDSCatalog.Icon = GetAbsouteUrl(XmlHelper.GetInnerText(xml.DocumentElement.SelectSingleNode("/feed/icon"), ""));

                    // Adding root node to GUI catalog tree
                    CatalogTree.Nodes.Clear();
                    TreeNode ParentNode = new TreeNode();
                    ParentNode.Text = currentOPDSCatalog.Title;
                    CatalogTree.Nodes.Add(ParentNode);           
    
                    currentOPDSCatalog.Entries = new List<OPDSEntry>();
                    foreach (XmlNode node in xml.DocumentElement.SelectNodes("/feed/entry"))
                    {
                        var catEntry = new OPDSEntry();
                        catEntry.Id = XmlHelper.GetInnerText(node.SelectSingleNode("id"), "");
                        if (catEntry.Id == "") continue;
                        catEntry.Updated = XmlHelper.GetInnerText(node.SelectSingleNode("updated"), "");
                        catEntry.Title = XmlHelper.GetInnerText(node.SelectSingleNode("title"), "");
                        catEntry.Content = XmlHelper.GetInnerText(node.SelectSingleNode("content"), "");
                        catEntry.Href = GetAbsouteUrl(XmlHelper.GetAttribute(node.SelectSingleNode("link"), "href", ""));
                        currentOPDSCatalog.Entries.Add(catEntry);

                        // Adding node to GUI catalog tree
                        TreeNode ChildNode = new TreeNode();
                        ChildNode.Text = catEntry.Title + " (" + catEntry.Content + ")";
                        ChildNode.Tag = catEntry;
                        ParentNode.Nodes.Add(ChildNode);
                    }
                    ParentNode.Toggle();

                }
                catch (Exception ex)
                {
                    showErrorMessage(ex.Message);
                }
            }
    
        }

        private bool IsAbsoluteUrl(string url)
        {
            if (url.Length<5)
            {
                return false;
            }
            if (url.Substring(0,5)=="http:" || url.Substring(0,6)=="https:")
            {
                return true;
            }
            return false;
        }

        private string GetAbsouteUrl(string url)
        {
            if (!IsAbsoluteUrl(url) && url!="")
            {
                return currentOPDSCatalogHost + @"/" + url;
            }
            else
            {
                return url;
            }
        }

        private void httpWorker_GetCatalogRoot(object sender, DoWorkEventArgs e)
        {
            currentOPDSCatalogHost = (new Uri(currentOPDSCatalog.Href)).GetLeftPart(UriPartial.Authority);

            try
            {
                var WebClient = new WebClient();
                WebClient.Encoding = Encoding.UTF8;
                CatalogHttpResult = WebClient.DownloadString(currentOPDSCatalog.Href);
            }
            catch (Exception ex)
            {

                CatalogHttpResult = null;
                showErrorMessage(ex.Message);
            }
            
        }

        private void showErrorMessage(string message)
        {
            MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private string removeInvalidChars(string filename)
        {
            var invalids = System.IO.Path.GetInvalidFileNameChars();
            return String.Join("_", filename.Split(invalids, StringSplitOptions.RemoveEmptyEntries)).TrimEnd('.');
        }

        private void httpWorker_GetCatalogEntryCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            httpWorker.DoWork -= httpWorker_GetCatalogEntry;
            httpWorker.RunWorkerCompleted -= httpWorker_GetCatalogEntryCompleted;

            if (e.Error != null)
            {
                showErrorMessage(e.Error.Message);
                return;
            }


            if (CatalogHttpResult != null)
            {
               try
               {
                    XmlDocument xml = new XmlDocument();
                    string filter = @"xmlns(:\w+)?=""([^""]+)""|xsi(:\w+)?=""([^""]+)""";   // replacing damn namespaces
                    CatalogHttpResult = Regex.Replace(CatalogHttpResult, filter, "");
                    CatalogHttpResult = CatalogHttpResult.Replace("dc:", "dc_").Replace("dcterms:", "dcterms_").Replace("calibre:", "calibre_").Replace("opensearch:", "opensearch_");

#if DEBUG
                    // saving entry xml to file
                    System.IO.File.WriteAllText( removeInvalidChars(SelectedEntry.Title) + ".xml", CatalogHttpResult);
#endif
                    xml.LoadXml(CatalogHttpResult);
                  

                    SelectedEntry.Entries = new List<OPDSEntry>();
                    foreach (XmlNode node in xml.DocumentElement.SelectNodes("/feed/entry"))
                    {
                        var catEntry = new OPDSEntry();
                        catEntry.Id = XmlHelper.GetInnerText(node.SelectSingleNode("id"), "");
                        if (catEntry.Id == "") continue;
                        catEntry.Updated = XmlHelper.GetInnerText(node.SelectSingleNode("updated"), "");
                        catEntry.Title = XmlHelper.GetInnerText(node.SelectSingleNode("title"), "");
                        catEntry.Content = XmlHelper.GetInnerText(node.SelectSingleNode("content"), XmlHelper.GetInnerText(node.SelectSingleNode("summary"), ""));

                        var fb2Link = XmlHelper.GetAttribute(node.SelectSingleNode("link[@type='application/fb2']"),
                                                             "href",
                                                             XmlHelper.GetAttribute(
                                                             node.SelectSingleNode(
                                                             "link[@type='application/fb2+zip']"), "href",
                                                             XmlHelper.GetAttribute(node.SelectSingleNode("link[@type='application/fb-ebook']"), "href", 
                                                             "")));

                        catEntry.Href = GetAbsouteUrl(XmlHelper.GetAttribute(node.SelectSingleNode("link"), "href", ""));
                        if (XmlHelper.GetInnerText(node.SelectSingleNode("dc_format"), "")!="" || 
                            XmlHelper.GetInnerText(node.SelectSingleNode("dc_language"), "")!="" ||
                            fb2Link != "")
                        {
                            catEntry.Downloadable = true;
                            var imageUrl = XmlHelper.GetAttribute(node.SelectSingleNode("link[@type='image/jpeg']"), 
                                           "href", 
                                           XmlHelper.GetAttribute(node.SelectSingleNode("link[@type='image/png']"),"href",""));
                            var imageUrlExt = System.IO.Path.GetExtension(imageUrl).ToLower();
                            if (imageUrlExt==".jpg" || imageUrlExt==".jpeg" || imageUrlExt==".gif" || imageUrlExt==".png")
                            {
                                catEntry.ImageUrl = GetAbsouteUrl(imageUrl);
                            }
                            else
                            {
                                catEntry.ImageUrl = "";
                            }
                            catEntry.DownloadLinkFb2 = GetAbsouteUrl(fb2Link);
                            catEntry.WebUrl = GetAbsouteUrl(XmlHelper.GetAttribute(node.SelectSingleNode("link[@type='text/html']"), "href", ""));
                        }
                        currentOPDSCatalog.Entries.Add(catEntry);

                        // Adding node to GUI catalog tree
                        TreeNode ChildNode = new TreeNode();
                        if (catEntry.Content.Length < 100 && catEntry.Content!="")
                        {
                            ChildNode.Text = catEntry.Title + " (" + formatContentText(catEntry.Content) + ")";
                        }
                        else
                        {
                            ChildNode.Text = catEntry.Title;
                        }
                        ChildNode.Tag = catEntry;
                        CatalogTree.SelectedNode.Nodes.Add(ChildNode);
                    }
                   CatalogTree.SelectedNode.Toggle();

               }
                catch (Exception ex)
               {
                    showErrorMessage(ex.Message);
               }
            }
        }

        private void httpWorker_GetCatalogEntry(object sender, DoWorkEventArgs e)
        {
            try
            {
                var WebClient = new WebClient();
                WebClient.Encoding = Encoding.UTF8;
                CatalogHttpResult = WebClient.DownloadString(SelectedEntry.Href);
            }
            catch (Exception ex)
            {

                CatalogHttpResult = null;
                showErrorMessage(ex.Message);
            }
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var webClient = new WebClient();
            var hreflink = SelectedEntry.DownloadLinkFb2;
            string filename = SelectedEntry.Title + ".zip";
            var dialog = new SaveFileDialog();
            dialog.Filter = "Archive (*.zip)|*.zip";
            dialog.FileName = filename;

            var result = dialog.ShowDialog(); //shows save file dialog
            if (result == DialogResult.OK)
            {
                webClient.DownloadFileAsync(new Uri(hreflink), dialog.FileName);
            }
            
        }

        private void CatalogTree_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (CatalogTree.SelectedNode != null && CatalogTree.SelectedNode.Tag != null && CatalogTree.SelectedNode.GetNodeCount(true) == 0)
            {
                SelectedEntry = (OPDSEntry)CatalogTree.SelectedNode.Tag;
                if (SelectedEntry.Downloadable)
                {
                    if (SelectedEntry.ImageUrl != "")
                    {
                        BookPictureBox.LoadAsync(SelectedEntry.ImageUrl);
                    }
                    else
                    {
                        BookPictureBox.Image = null;
                    }
                    DownloadFB2Link.Text = SelectedEntry.DownloadLinkFb2;
                    WebUrlLink.Text = SelectedEntry.WebUrl;
                }
                else
                {
                    if (!httpWorker.IsBusy)
                    {
                        httpWorker.DoWork += httpWorker_GetCatalogEntry;
                        httpWorker.RunWorkerCompleted += httpWorker_GetCatalogEntryCompleted;
                        httpWorker.RunWorkerAsync();
                    }
                }
            }
        }

        private void CatalogTree_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (CatalogTree.SelectedNode != null && CatalogTree.SelectedNode.Tag != null && ((OPDSEntry)CatalogTree.SelectedNode.Tag).Downloadable==true)
            {
                SelectedEntry = (OPDSEntry)CatalogTree.SelectedNode.Tag;
                if (SelectedEntry.Downloadable)
                {
                    if (SelectedEntry.ImageUrl != "")
                    {
                        BookPictureBox.LoadAsync(SelectedEntry.ImageUrl);
                    }
                    else
                    {
                        BookPictureBox.Image = null;
                    }
                    DownloadFB2Link.Text = SelectedEntry.DownloadLinkFb2;
                    WebUrlLink.Text = SelectedEntry.WebUrl;
                    contentTextBox.Text = formatContentText(SelectedEntry.Content);
                }
            }
        }

        private string formatContentText(string content)
        {
            return content.Replace("</empty-line>", "").
                           Replace("<p>", Environment.NewLine);
        }

        private void WebUrlLink_MouseClick(object sender, MouseEventArgs e)
        {
            var selectedNode = CatalogTree.SelectedNode;
            if (CatalogTree.SelectedNode != null && CatalogTree.SelectedNode.Tag != null && CatalogTree.SelectedNode.GetNodeCount(true) == 0)
            {
                SelectedEntry = (OPDSEntry) CatalogTree.SelectedNode.Tag;
                System.Diagnostics.Process.Start(SelectedEntry.WebUrl);
            }
        }

        private void CatalogsComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            PopulateCatalogTree();
        }
        
    }
}
